#include <gl/freeglut.h>
#include "Balloon.h"
#include "math.h"
#include "Utilities.h"
#include <stdlib.h>
#include "glm.hpp"

GLuint display_list_balloon_handle;

#define MAXOUTLINES 23.0 

//used to mimic vector
struct point3D{ 
	float x; 
	float y; 
	float z; 
}; 

bool balloonDead = false;

/*
	This function is used to display the balloon. It uses a display list after the first round.
	If a ballon is dead (has been hit by a ducky) it redraws the balloon each display (because it is of
	different size each time), due to the nature of the destruction sequence of the balloon.
*/
void Balloon::DisplayBalloon(void){

	GLfloat	ambient[4]	= { 0.5f , 0.5f , 0.5f , 1.00f };
	GLfloat	diffuse[4]	= { 0.5f , 0.5f , 0.5f , 1.00f };
	GLfloat	specular[4]	= { 1.00f , 1.00f , 1.00f , 1.00f };

	glMaterialf( GL_FRONT , GL_SHININESS , 128.0f);
	glMaterialfv(GL_FRONT , GL_AMBIENT   , ambient);
	glMaterialfv(GL_FRONT , GL_DIFFUSE   , diffuse);
	glMaterialfv(GL_FRONT , GL_SPECULAR  , specular);
	glShadeModel(GL_SMOOTH);

	//global
	glPushMatrix();
	glTranslatef(x,y,z);

	//if the display list hasn't been intialized yet...
	if(display_list_balloon_handle == (GLuint)-1){
		display_list_balloon_handle = glGenLists(1);
		glNewList(display_list_balloon_handle, GL_COMPILE);

		//local
		glPushMatrix();
		glColor3f(1,0,0);
		glScalef(1, (GLfloat)(1.2), 1);
		glPushMatrix();
		BalloonBase();
		glPopMatrix();
		glTranslated(0, 1.98, 0);
		glEndList();
	}

	//call displaylist if balloon is alive and well
	if(!balloonDead)
	{
		glCallList(display_list_balloon_handle);
	}
	//if the balloon is currently being destroyed it must be redrawn each time manually.
	else
	{
		glPushMatrix();
		glColor4d(1,0,0,1);
		glScalef(1, (GLfloat)(1.2), 1);
		glPushMatrix();
		BalloonBase();
		glPopMatrix();
		glTranslated(0, 1.98, 0);
	}
	glClearColor(1.0,1.0,1.0,1.0);
	glPopMatrix();
	glPopMatrix();
}

/*
	Constructor for balloon that defaults the location of the balloon to (0, 0, 0)
	bV = bottom vertices = the number of vertices to be used for the sine wave portion of the balloon
	mV = middle vertices = the number of vertices to be used for the bottom circle portion of the balloon
	tV = top vertices = the number of vertices to be used for the top circle portion of the balloon
	
	All other variables are initialized accordingly.
*/
Balloon::Balloon(int bV, int mV, int tV)
{		
	display_list_balloon_handle = -1;
	balloonDead = false;
	//vertice calculation
	bottomV = bV;
	middleV = mV;
	topV = tV;
	numVertices = bV + mV + tV;
	findVertices();
	//location of balloon set to default
	x = 0;
	y = 0;
	z = 0;
	//minimum points on a ballon is 10 (set to this in this constuctor due to lack of coordinates)
	points = 10;
}

/*
	Constructor for balloon that sets the location of the balloon to (x1, y1, z1)
	bV = bottom vertices = the number of vertices to be used for the sine wave portion of the balloon
	mV = middle vertices = the number of vertices to be used for the bottom circle portion of the balloon
	tV = top vertices = the number of vertices to be used for the top circle portion of the balloon
	
	All other variables are initialized accordingly.
*/
Balloon::Balloon(int bV, int mV, int tV, float x1, float y1, float z1){
	display_list_balloon_handle = -1;
	balloonDead = false;
	//sets location
	x = x1;
	y = y1;
	z = z1;
	//points calculated using simple algorithm based on distance
	points = (int)((abs(x) + abs(y))*abs(z));
	if(points == 0){
		points = 10;
	}
	if(points > 99){
		points = 99;
	}
	//calculate vertices
	bottomV = bV;
	middleV = mV;
	topV = tV;
	numVertices = bottomV + middleV + topV;
	findVertices();
}

/*
	Draws the entire balloon from the vertex array. A torus is drawn first and then the vertex array calculated from findVertices()
	The balloon is drawn using a surface of revolution. The dot products and normals are calculated and set. numVertices is the total
	number of vertices.

	References: http://csclab.murraystate.edu/bob.pilgrim/515/lectures_05.html
	*/
void Balloon::BalloonBase(void){
	int ang,i;  
	int delang = 10;
	pi = (float)(3.1419);
	float x1,x2,z1,z2,x3,z3; 
	float *normal = new float[3];
	glm::vec3 a1;
	glm::vec3 a2;
	glm::vec3 n;
	//first draw the torus
	glPushMatrix();
	glTranslatef(0,(GLfloat)(-.032),0);
	glRotated(90,1,0,0);
	glutSolidTorus(.02,.04,10,10);
	glPopMatrix();

	for (i=0;i<numVertices-1;i++) 
	{ 
		glBegin(GL_TRIANGLE_STRIP); 
		for(ang=0;ang<=360;ang+=delang)  
		{  
			x1=(float)(hemx[i]*cos((double)ang*2.0*pi/360.0)); 
			x2=(float)(hemx[i+1]*cos((double)ang*2.0*pi/360.0)); 
			z1=(float)(hemx[i]*sin((double)ang*2.0*pi/360.0));  
			z2=(float)(hemx[i+1]*sin((double)ang*2.0*pi/360.0));  
			x3=(float)(hemx[i]*cos((double)(ang+delang)*2.0*pi/360.0)); 
			z3=(float)(hemx[i]*sin((double)(ang+delang)*2.0*pi/360.0)); 
            // difference between p2 and p1 
			a1.x =x2-x1; 
			a1.y = hemy[i+1]-hemy[i];
			a1.z = z2 - z1;

			// difference between p3 and p1                  
			a2.x = x3 - x1;
			a2.y = hemy[i+1] - hemy[i];
			a2.z = z3 - z1;
			//compute the normals
			n = glm::normalize(glm::cross(a1, a2));
			normal[0] = n.x;
			normal[1] = n.y;
			normal[2] = n.z;
			//set the normals (for lighting)
			glNormal3fv(normal);  
			glVertex3f(x1,hemy[i],z1);  
			glVertex3f(x2,hemy[i+1],z2); 
		}; 
		glEnd();  
	}
	delete[] normal;
}

/*
	calculate a vector's normal
	no longer utilized but kept in project to remind ourselves how to do it...
	it can be reactivated at any time.
*/
void Balloon::normalize(float v[3])  
{  
	float d = sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);  
	if (d != 0.0) 
	{  
		v[0]/=d; 
		v[1]/=d;  
		v[2]/=d;  
	}  
} 

/*
	calculates the cross product of 2 vectors and returns the result
	no longer utilized but kept in project to remind ourselves how to do it...
	it can be reactivated at any time.
*/
void Balloon::normCrossProd(float v1[3], float v2[3], float out[3])  
{  
	out[0] = v1[1]*v2[2] - v1[2]*v2[1];  
	out[1] = v1[2]*v2[0] - v1[0]*v2[2];  
	out[2] = v1[0]*v2[1] - v1[1]*v2[0];  
	normalize(out);  
} 

/*
	finds the vertices in 3 parts
	1st part - partial sine wave y = sin(x)
	2nd part - lower part of circle equation x^2 + (y-1)^2 = 1
	3rd part - upper part of circle equation x^2 + (y-1)^2 = 1
	puts all the vertices into a giant array of vertices to be draw via surface of revolution
*/
void Balloon::findVertices(){
	//calculate sine from 0 to pi/2
	//i starts at two for pi/2 purposes
	hemx = new float[numVertices];
	hemy = new float[numVertices];
	//copy of array for balloon recovery
	copyX = new float[numVertices];
	copyY = new float[numVertices];
	//we want this to be n units wide
	float n = (float)(.85);
	float increment = (float)(n/bottomV);
	float currX = 0.0;
	int i;
	for (i = 0; i < bottomV; i++){
		currX = increment*i;
		//ensures balloon does not come to a point at bottom
		if(currX < .04){
			currX = (float)(.04);
		}
		hemy[i] = (float)(sin((float)(currX))-.06);
		hemx[i] = currX;

		copyY[i] = (float)(sin((float)(currX))-.06);
		copyX[i] = currX;
	}

	float xbase = (float)0.85;
	float ybase = .75;
	float lastValue;
	float lastValueX;

	//calculate the lower part of the circle
	//equation for y = 1 - sqrt(1-x^2)
	for(i=0; i < middleV; i++){
		float xincrement = (float)((1.085-xbase)/middleV);
		xbase = xbase + xincrement;
		float y = (float)(1 - sqrt(1.085- pow(xbase, 2))+.27);
		hemx[bottomV+i] = (float)(xbase-.025);
		hemy[bottomV+i] = y;

		copyX[bottomV+i] = (float)(xbase-.025);
		copyY[bottomV+i] = y;

		//set last value to ensure no gap between next part
		if(i == middleV-1){
			lastValue = y;
			lastValueX = x;
		}
	}	

	//calculate the top part of the circle
	//equation for y = 1 + sqrt(1-x^2)
	for (i = 0; i < topV; i++){
		float multiple = (float)lastValue / (float)topV;
		float x = multiple * (topV-i);
		float y = (float)(1 + sqrt(1- pow(x, 2))-.11);
		hemx[bottomV+middleV+i] = x;
		copyX[bottomV+middleV+i] = x;
		//ensure that the balloon top closes (no gaps)
		if(i == topV-1){
			hemx[bottomV+middleV +i] = 0.0;
			copyX[bottomV+middleV +i] = 0.0;
		}
		hemy[bottomV+middleV+i] = y;
		copyY[bottomV+middleV+i] = y;
	}
}

/*
	Upon being hit, each balloon undergoes a custom death sequence that is best described as an implosion.
	This method uses randoms to manipulate the vertex array.
*/
void Balloon::initiateDeathSequence(void){
	balloonDead = true;
	int i;
	for(i =0; i< numVertices; i++){
		y = (float)(y - .01);
		float r1 = (float)rand()/(float)RAND_MAX;
		float r2 = (float)(rand() % 2);
		
		if(r2 == 1 && hemx[i] > 0)
			hemx[i] = (float)(hemx[i]-.1);
		else if(r2 == 0 && hemy[i] > 0)
			hemy[i] = (float)(hemy[i]-.1);
	}
}

/*
	In order to save ourselves valuable computation time each balloon is recycled. Each vertex array is copied originally
	so that upon destruction it can be recovered without manually resetting each value in the vertex array. This copy is
	then just copied back into the vertex array and the balloon is restored to its former glory.
*/
void Balloon::recoverDeadBalloon(){
	int i;
	balloonDead = false;
	for(i=0;i<numVertices;i++){
		hemx[i] = copyX[i];
		hemy[i] = copyY[i];
	}
}

/*
	This method displays the points of each balloon directly centered and above the balloon. It uses a dynamic equation that takes
	into account the z distance and adjusts the x and y accordingly. Points are drawn orthogonally so they always face the player.
*/
void Balloon::drawPoints(void *font)
{		glClearColor(1.0,1.0,1.0,1.0);
		 glColor4d(0, 0, 0, 1); 
		 char s[8];// = convertInt(points);
		 _itoa_s(points, s, 10);
		 //the nearer it is to the viewer, the more x factors in, the higher y must be.
		 //the further away from the viewer, the less x factors in, the lower y must be.
		 float x2 = (float)(abs(x/z)/2+.5);
		 float y2 = (float)(abs(y/z)+2.5);
		 float z2 = abs(z/z);

		glRasterPos3f(x-x2, y+y2,z);
		int i;
		for (i = 0; i < (int)(strlen(s)); i++) 
			{
				glutBitmapCharacter(font,s[i]);
			}
}
/*
	returns the balloon's points
*/
int Balloon::getPoints(){
	return points;
}

/*
	returns the balloon's x location
*/
double Balloon::getXLocation(void){
	return x;
}

/*
	returns the balloon's y location
*/
double Balloon::getYLocation(void){
	return y;
}

/*
	returns the balloon's z location
*/
double Balloon::getZLocation(void){
	return z;
}

/*
	sets the balloon's (x,y,z) location and adjusts the points based off the coordinates
*/
void Balloon::setLocation(float xLoc, float yLoc, float zLoc){
	x = xLoc;
	y = yLoc;
	z = zLoc;
	points = (int)((abs(x)+abs(y))*abs(z)/2);
	if(points < 10){
		points = 10;
	}
	if (points > 99){
		points = 99;
	}
}
